package bjut.core.proxy;

import bjut.core.Message.RpcRequest;
import bjut.core.Message.RpcResponse;
import bjut.core.config.ProviderConfig;
import bjut.core.enums.RpcResponseEnum;
import bjut.core.interfaces.ZRpcRequestTransport;
import bjut.core.netty.NettyRpcClientHandler;
import bjut.core.netty.ZRpcNettyClient;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
 * @Author: zhuzhiming
 * @Date: 2022/5/7 19:28
 */
public class ZRpcClientProxy implements InvocationHandler {

    private final ZRpcRequestTransport rpcRequestTransport;
    private final ProviderConfig providerConfig;

    public ZRpcClientProxy(ZRpcRequestTransport rpcRequestTransport, ProviderConfig providerConfig) {
        this.rpcRequestTransport = rpcRequestTransport;
        this.providerConfig = providerConfig;
    }

    public <T> T getProxy(Class<T> cls) {
        return (T) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls}, this);
    }

    /*
    *    getDeclaringClass
       该方法返回一个Class对象，返回当前class对象的声明对象class,一般针对内部类的情况，比如A类有内部类B，那么通过B.class.getDeclaringClass()方法将获取到A的Class对象.
      在使用反射对象时比如Method和Field的getDeclaringClass方法将获取到所属类对象

    *   getDeclaredClasses
       该方法返回当前Class声明的public，private ，default，private的内部类

    * *   getClasses
      该方法获取的是包含父类和当前类声明的public类型的内部类，注意是public的
    * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        RpcRequest rpcRequest = RpcRequest.builder()
                .requestId(UUID.randomUUID().toString())
                .interfaceName(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .parameters(args)
                .paramTypes(method.getParameterTypes())
                .group(providerConfig.getGroup())
                .version(providerConfig.getVersion())
                .build();


        RpcResponse<Object> rpcResponse = null;

        if (rpcRequestTransport instanceof ZRpcNettyClient) {
            CompletableFuture<RpcResponse<Object>> completableFuture = (CompletableFuture<RpcResponse<Object>>) rpcRequestTransport.sendRpcRequest(rpcRequest);
            rpcResponse = completableFuture.get();
        }
        check(rpcRequest, rpcResponse);
        return rpcResponse.getData();
    }

    private void check(RpcRequest rpcRequest, RpcResponse<Object> rpcResponse) {

        if (rpcResponse == null) {
            throw new RuntimeException("call service but rpcResponse is null");
        }
        if (!rpcRequest.getRequestId().equals(rpcResponse.getRequestId())) {
            throw new RuntimeException("request id not equal response id");
        }

        if (rpcResponse.getCode() == null || !rpcResponse.getCode().equals(RpcResponseEnum.SUCCESS.getCode())) {
            throw new RuntimeException("call service fail");
        }


    }
}
